﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

using Qing.Lang;
using Microsoft.VisualBasic;

namespace Qing.Std {


    class Pwd : Native {
        public override string Name { get; set; } = "当前目录";
        public override string Desc { get; set; } = "无参，返回字符串；获取当前程序运行所处的目录";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {

            return new Expr(TP.Str, Directory.GetCurrentDirectory());

        }

    }


    class Exists: Native {
        public override string Name { get; set; } = "文件是否存在";
        public override string Desc { get; set; } = "参数1-字符串，返回逻辑；判断参数1对应的文件是否存在";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count == 0) {
                return Expr.Err("文件是否存在函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("文件是否存在函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;
                
            }

            if (path.EndsWith("/")) {
                return new Expr(TP.Bool, Directory.Exists(path));
            } else {
                return new Expr(TP.Bool, File.Exists(path));
            }

        }

    }


    class ReadBinFile : Native {
        public override string Name { get; set; } = "读二进制文件";
        public override string Desc { get; set; } = "参数1-字符串，返回二进制；根据参数1的路径，读取文件的二进制数据";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count == 0) {
                return Expr.Err("读二进制文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("读二进制文件函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;

            }

            if (!File.Exists(path)) {
                return Expr.Err("读取的文件不存在");
            }

            return new Expr(TP.Bin, File.ReadAllBytes(path));

        }

    }

    class ReadTextFile : Native {
        public override string Name { get; set; } = "读文本文件";
        public override string Desc { get; set; } = "参数1-字符串，返回字符串；根据参数1的路径，读取文件的文本数据";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count == 0) {
                return Expr.Err("读文本文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("读文本文件函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;

            }

            if (!File.Exists(path)) {
                return Expr.Err("读取的文件不存在");
            }

            return new Expr(TP.Str, File.ReadAllText(path));

        }

    }

    class WriteBinFile : Native {
        public override string Name { get; set; } = "写二进制文件";
        public override string Desc { get; set; } = "参数1-字符串，参数2-二进制，可选参数3-逻辑，返回逻辑；根据参数1的路径，将参数2的二进制数据写出到文件中，参数3为真时表示追加写入（默认覆盖）";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 2) {
                return Expr.Err("写二进制文件函数调用参数不足");
            }
            if (args[1].Tp != TP.Bin) {
                return Expr.Err("写二进制文件函数参数错误");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("写二进制文件函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;

            }

            if (!File.Exists(path)) {
                File.Create(path).Close();
            }
            bool append = false;
            if (args.Count > 2) {
                append = args[2].ToBool();
            }

            bool finsh = false;
            using (BinaryWriter bw = new BinaryWriter(new FileStream(path, append ? FileMode.Create | FileMode.Append : FileMode.Create, FileAccess.Write))) {

                bw.Write(args[1].Bin());
                finsh = true;

            }


            return new Expr(TP.Bool, finsh);

        }

    }


    class WriteTextFile : Native {
        public override string Name { get; set; } = "写文本文件";
        public override string Desc { get; set; } = "参数1-字符串，参数2-字符串，可选参数3-逻辑，返回逻辑；根据参数1的路径，将参数2的字符串写出到文件中，参数3为真时表示追加写入（默认覆盖）";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 2) {
                return Expr.Err("写文本文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("写文本文件函数的参数文件名必须为字符串");
            }
            if (args[1].Tp != TP.Str) {
                return Expr.Err("写二进制文件函数参数错误");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;

            }

            if (!File.Exists(path)) {
                File.Create(path).Close();
            }
            bool append = false;
            if (args.Count > 2) {
                append = args[2].ToBool();
            }

            bool finsh = false;
            using (BinaryWriter bw = new BinaryWriter(new FileStream(path, append ? FileMode.Create | FileMode.Append : FileMode.Create, FileAccess.Write))) {
               
                bw.Write(System.Text.Encoding.Default.GetBytes(args[1].Str()));
                finsh = true;
                
            }
       

            return new Expr(TP.Bool, finsh);

        }

    }




    class RenameFile : Native {
        public override string Name { get; set; } = "文件重命名";
        public override string Desc { get; set; } = "参数1-字符串，参数2-字符串，返回逻辑；将参数1路径对应的文件名改为参数2对应的文件名";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 2) {
                return Expr.Err("文件重命名函数调用参数不足");
            }
            if (args[0].Tp != TP.Str || args[1].Tp != TP.Str) {
                return Expr.Err("文件重命名函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (path.EndsWith('/') && Directory.Exists(path)) {

                try {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
                        FileSystem.Rename(path, path + "../" + args[1].Str());
                    } else {
                        DirectoryInfo di = new DirectoryInfo(path);
                        di.MoveTo(path + "../" + args[1].Str());
                    }
                } catch (Exception e) {
                    return Expr.Err(e.Message);
                }

                return new Expr(TP.Bool, true);
            } else if (!path.EndsWith('/') && File.Exists(path)) {

                try {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
                        FileSystem.Rename(path, path + "/../" + args[1].Str());
                    } else {
                        FileInfo fi = new FileInfo(path);
                        fi.MoveTo(path + "/../" + args[1].Str());
                    }
                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);

            } else {
                return new Expr(TP.Bool, false);
            }

        }

    }



    class MoveFile : Native {
        public override string Name { get; set; } = "移动文件";
        public override string Desc { get; set; } = "参数1-字符串，参数2-字符串，返回逻辑；将参数1路径对应的文件移动到参数2对应的路径";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 2) {
                return Expr.Err("移动文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str || args[1].Tp != TP.Str) {
                return Expr.Err("移动文件函数的参数文件名必须为字符串");
            }

            string fp0 = args[0].Str();
            string fp1 = args[1].Str();

            if (!File.Exists(fp0)) {
                return Expr.Err("源文件不存在");
            }


            if (fp0.EndsWith('/') && Directory.Exists(fp0)) {
                try {
                    DirectoryInfo di = new DirectoryInfo(fp0);
                    di.MoveTo(fp1);
                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);

            } else if (!fp0.EndsWith('/') && File.Exists(fp0)) {
                try {
                    FileInfo fi = new FileInfo(fp0);
                    fi.MoveTo(fp1);

                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);
            } else {
                return new Expr(TP.Bool, false);
            }

        }

    }




    class CopyFile : Native {
        public override string Name { get; set; } = "复制文件";
        public override string Desc { get; set; } = "参数1-字符串，参数2-字符串，返回逻辑；将参数1路径对应的文件复制到参数2对应的路径";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 2) {
                return Expr.Err("移动文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str || args[1].Tp != TP.Str) {
                return Expr.Err("移动文件函数的参数文件名必须为字符串");
            }

            string fp0 = args[0].Str();
            string fp1 = args[1].Str();

            bool rewrite = false;
            if(args.Count > 2) {
                rewrite = args[2].ToBool();
            }

            if (fp0.EndsWith('/') && fp1.EndsWith('/') && Directory.Exists(fp0)) {
                return CopyFolder(fp0, fp1, rewrite);

            } else if (!fp0.EndsWith('/') && !fp1.EndsWith('/') && File.Exists(fp0)) {
                try {
                    System.IO.File.Copy(fp0, fp1, rewrite);

                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);
            } else {
                return new Expr(TP.Bool, false);
            }

        }

        public static Expr CopyFolder(string sourceFolder, string destFolder, bool rewrite) {
            try {
                //如果目标路径不存在,则创建目标路径
                if (!System.IO.Directory.Exists(destFolder)) {
                    System.IO.Directory.CreateDirectory(destFolder);
                }
                //得到原文件根目录下的所有文件
                string[] files = System.IO.Directory.GetFiles(sourceFolder);
                foreach (string file in files) {
                    string name = System.IO.Path.GetFileName(file);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    System.IO.File.Copy(file, dest, rewrite);//复制文件
                }
                //得到原文件根目录下的所有文件夹
                string[] folders = System.IO.Directory.GetDirectories(sourceFolder);
                foreach (string folder in folders) {
                    string name = System.IO.Path.GetFileName(folder);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    CopyFolder(folder, dest, rewrite);//构建目标路径,递归复制文件
                }
                return new Expr(TP.Bool, true);
            } catch (Exception e) {
                return Expr.Err( e.Message);
            }

        }
    }



    class DeleteFile : Native {
        public override string Name { get; set; } = "删除文件";
        public override string Desc { get; set; } = "参数1-字符串，返回逻辑；删除参数1路径对应的文件，删除成功返回真，否则返回假";
        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 1) {
                return Expr.Err("删除文件函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("删除文件函数的参数文件名必须为字符串");
            }

            string path = args[0].Str();
            if (path.EndsWith('/') && Directory.Exists(path)) {
                try {
                    DirectoryInfo di = new DirectoryInfo(args[0].Str());
                    di.Delete(true);

                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);
            } else if (!path.EndsWith(path) && File.Exists(path)) {
                try {
                    FileInfo fi = new FileInfo(args[0].Str());
                    fi.Delete();

                } catch (Exception e) {
                    return Expr.Err( e.Message);
                }

                return new Expr(TP.Bool, true);

            } else {
                return new Expr(TP.Bool, false);
            }

        }

    }




    class Fcd : Native {
        public override string Name { get; set; } = "切换目录";
        public override string Desc { get; set; } = "参数1-字符串，返回逻辑；将当前程序运行所处的目录改为参数1对应的路径";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 1) {
                return Expr.Err("切换目录函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("切换目录函数的参数路径必须为字符串");
            }

            string path = args[0].Str();
            if (!path.EndsWith('/')) {
                return Expr.Err( "切换目录函数的参数必须是合法的目录路径");
            }

            if (Directory.Exists(path)) {
                Directory.SetCurrentDirectory(path);
                return new Expr(TP.Bool, true);
            } else {
                return new Expr(TP.Bool, false);
            }

        }

    }


    class Fdir : Native {
        public override string Name { get; set; } = "创建目录";
        public override string Desc { get; set; } = "参数1-字符串，返回逻辑；根据参数1对应的路径，创建新的目录";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 1) {
                return Expr.Err("创建目录函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("创建目录函数的参数路径必须为字符串");
            }

            string path = args[0].Str();
            if (!path.EndsWith('/')) {
                return Expr.Err( "创建目录函数的参数必须是合法的目录路径");
            }

            if (Directory.Exists(path)) {
                return new Expr(TP.Bool, false);
            } else {
                Directory.CreateDirectory(path);
                return new Expr(TP.Bool, true);
            }

        }

    }


    class Fls : Native {
        public override string Name { get; set; } = "目录列表";
        public override string Desc { get; set; } = "参数1-字符串，可选参数2-逻辑，返回字符串数组；根据参数1对应的路径找到目录，返回目录下所有的文件名，如果参数2为真则返回的是绝对路径";

        public static string TakeLastName(string path) {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){
                string[] arr = path.Split('\\');
                return arr[arr.Length-1];
            } else {
                string[] arr = path.Split('/');
                return arr[arr.Length-1];
            }
        }

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            
            string path = "";
            if(args.Count > 0 && args[0].Tp == TP.Str) {
                path = args[0].Str();
                if (!args[0].Str().StartsWith('/') && !args[0].Str().Contains(':')) {
                    path = Directory.GetCurrentDirectory() + '/' + args[0].Str();
                } 
                             
            }

            if (!Directory.Exists(path)) {
                return Expr.Err( "目录列表函数的参数目录路径不存在");
            }

            string[] files = Directory.GetFiles(path);
            string[] dirs = Directory.GetDirectories(path);
            List<Expr> list = new List<Expr>();

            if(args.Count > 1 && args[1].ToBool()) {
                foreach (var item in files) {
                    list.Add(new Expr(TP.Str, item));
                }
                foreach (var item in dirs) {
                    list.Add(new Expr(TP.Str, item + "/"));
                }
            } else {
                foreach (var item in files) {
                    list.Add(new Expr(TP.Str, TakeLastName(item)));
                }
                foreach (var item in dirs) {
                    list.Add(new Expr(TP.Str, TakeLastName(item) + "/"));
                }

            }
            

            Expr ret = new Expr(TP.Arr, list);
            ret.List = list;
            return ret;
        }

    }



    class Fabs : Native {
        public override string Name { get; set; } = "文件绝对路径";
        public override string Desc { get; set; } = "参数1-字符串，返回字符串；返回参数1对应的绝对路径";

        public override Expr Run(List<Expr> args, Ctx ctx, Obj? obj=null, List<Expr>? namedArgs=null) {
            if (args.Count < 1) {
                return Expr.Err("文件绝对路径函数调用参数不足");
            }
            if (args[0].Tp != TP.Str) {
                return Expr.Err("文件绝对路径函数的参数路径必须为字符串");
            }

            string path = args[0].Str();
            if (!path.StartsWith("/") && !path.Contains(":")) {
                path = Directory.GetCurrentDirectory() + '/' + path;
            }

            return new Expr(TP.Str, Path.GetFullPath(path));
            

        }

    }




}
